#ifndef UNIT_HPP_INCLUDED
#define UNIT_HPP_INCLUDED

#include <boost/intrusive_ptr.hpp>

#include "formula_callable.hpp"
#include "pathfind.hpp"
#include "terrain_logic.hpp"
#include "tile_logic.hpp"
#include "unit_type.hpp"
#include "wml_node_fwd.hpp"

namespace game {

class player;

class unit;
typedef boost::intrusive_ptr<unit> unit_ptr;
typedef boost::intrusive_ptr<const unit> const_unit_ptr;

class commander_unit;
typedef boost::intrusive_ptr<commander_unit> commander_unit_ptr;
typedef boost::intrusive_ptr<const commander_unit> const_commander_unit_ptr;

class soldier_unit;
typedef boost::intrusive_ptr<soldier_unit> soldier_unit_ptr;
typedef boost::intrusive_ptr<const soldier_unit> const_soldier_unit_ptr;

class unit : public game_logic::formula_callable, public hex::path_cost_calculator {
public:
	unit();
	explicit unit(wml::const_node_ptr node);
	virtual ~unit();
	std::string description() const;

	int attack() const;
	int defense() const;
	int aggression() const;
	int movement() const;
	int attack_range() const;

	void increase_attack(int amount);
	void increase_defense(int amount);

	unit_type::FIGHTING_STYLE fighting_style() const;

	int terrain_defense_bonus(const hex::location& loc) const;
	int terrain_defense_bonus(const_base_terrain_ptr terrain) const;

	virtual void draw(int* pixel_x=0, int* pixel_y=0) const;
	virtual const unit_type& type() const = 0;

	const hex::location& loc() const { return loc_; }
	void set_loc(const hex::location& loc);

	int moves_left() const { return moves_left_; }
	void restore_movement() { moves_left_ = movement(); }
	int hitpoints() const { return hitpoints_; }
	void get_hit(int damage=1);
	void heal(int amount);
	bool destroyed() const;

	virtual commander_unit& get_commander() = 0;
	virtual const commander_unit& get_commander() const = 0;

	virtual void new_turn();

	//will mark the unit as selected for this frame.
	void set_selected();

	virtual int movement_cost(const hex::location& a, const hex::location& b) const;
	virtual int estimated_cost(const hex::location& a, const hex::location& b) const;
	virtual bool allowed_to_move(const hex::location& a) const;
	virtual bool legal_move_endpoint(const hex::location& a) const;

	void use_moves(int moves);
	void move_along_path(const std::vector<hex::location>& path);
	const std::vector<hex::location>& current_move_path() const { return move_path_; }
	bool is_moving() const { return move_path_.empty() == false; }

	virtual const std::string& portrait() const;

	UnitCategory categories() const { return type().categories(); }

	const std::vector<const_unit_modification_ptr>& modifications() const { return type().modifications(); }

	virtual bool is_commander() const { return false; }
private:
	int move_cost(const_base_terrain_ptr terrain) const;
	int move_cost(const hex::location& loc) const;

	virtual variant get_value(const std::string& key) const;

	hex::location loc_;
	int hitpoints_;
	int moves_left_;

	//sets the cycle on which the unit was selected at. If equal to the
	//current cycle, then the unit is now drawn as selected.
	int selected_at_;

	mutable std::vector<hex::location> move_path_;
	mutable int move_path_index_;
	mutable int percent_along_path_;

	std::string description_;

	int attack_increase_, defense_increase_;
};

class commander_unit : public unit {
public:
	explicit commander_unit(player& owner, wml::const_node_ptr node);
	~commander_unit();

	player& owner() { return *owner_; }
	const player& owner() const { return *owner_; }
	void set_owner(player& p);
	const unit_type& type() const { return *type_; }

	const std::string& id() const { return id_; }

	const std::vector<soldier_unit_ptr>& soldiers() const { return soldiers_; }
	void add_soldier(soldier_unit_ptr soldier);
	void clear_soldiers();

	int commands_left() const { return commands_left_; }
	int num_commands() const;
	int command_radius() const;
	int attack_bonus() const;
	int defense_bonus() const;

	commander_unit& get_commander();
	const commander_unit& get_commander() const;

	void new_turn();

	//will mark the commander's squad as selected for the remainder
	//of this frame.
	void set_squad_selected();

	bool is_squad_selected() const;

	void draw(int* pixel_x=0, int* pixel_y=0) const;

	bool use_command();
	void remove_dead_soldiers();

	std::vector<const_soldier_unit_type_ptr> recruits() const;

	const std::string& portrait() const;

	bool is_commander() const { return true; }

	bool award_experience(int amount=1);
	int level() const { return level_; }
	int experience() const { return experience_; }
	int experience_required() const;
private:
	player* owner_;
	const_commander_unit_type_ptr type_;

	std::string id_;

	std::vector<soldier_unit_ptr> soldiers_;

	int commands_left_;

	//marks when a unit of the squad was selected. All units in this
	//commander's squad will be marked.
	int squad_selected_at_;

	std::string portrait_;

	int attack_bonus_increase_, defense_bonus_increase_;
	int command_radius_increase_;

	int level_, experience_;
};

class soldier_unit : public unit {
public:
	soldier_unit(commander_unit& commander, const_soldier_unit_type_ptr type);
	soldier_unit(commander_unit& commander, wml::const_node_ptr node);
	const unit_type& type() const { return *type_; }

	commander_unit& get_commander();
	const commander_unit& get_commander() const;

	void new_turn();

private:
	commander_unit& commander_;
	const_soldier_unit_type_ptr type_;
};

}

#endif
